/**
 @file sys_duet2_port.c

 @author  Copyright (C) 2011 Centec Networks Inc.  All rights reserved.

 @date 2020-11-14

 @version v2.0

*/

/****************************************************************************
 *
* Header Files
*
****************************************************************************/
#include "sal.h"
#include "ctc_error.h"
#include "ctc_debug.h"
#include "ctc_interrupt.h"
#include "ctc_warmboot.h"
#include "sys_usw_common.h"
#include "sys_usw_datapath.h"
#include "sys_usw_port.h"
#include "sys_usw_chip.h"
#include "sys_usw_peri.h"
#include "sys_usw_stats_api.h"
#include "sys_usw_dmps.h"
#include "sys_usw_interrupt.h"
#include "sys_usw_qos.h"
#include "sys_usw_port.h"
#include "sys_usw_wb_common.h"
#include "sys_usw_mcu.h"
#include "sys_duet2_port.h"
#include "sys_duet2_mac.h"
#include "drv_api.h"
#include "usw/include/drv_common.h"


/****************************************************************************
 *
* Defines and Macros
*
*****************************************************************************/

/****************************************************************************
 *
* Function
*
*****************************************************************************/
int32
sys_duet2_set_min_frame_size(uint8 lchip, ctc_frame_size_t index, uint16 value)
{
    int32  step = 0;
    uint32 cmd;
    uint32 field = 0;
    ds_t   ds;
    SYS_PORT_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n", __FUNCTION__);

    field = value;
    step = NetRxLenChkCtl_cfgMinLen1_f - NetRxLenChkCtl_cfgMinLen0_f;
    cmd = DRV_IOR(NetRxLenChkCtl_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &ds));
    SetNetRxLenChkCtl(V, cfgMinLen0_f + (step * index),&ds, field);
    cmd = DRV_IOW(NetRxLenChkCtl_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &ds));

    return CTC_E_NONE;
}

int32
sys_duet2_get_min_frame_size(uint8 lchip, ctc_frame_size_t index, uint16* p_value)
{
    int32  step = 0;
    uint32 cmd;
    uint32 field = 0;
    ds_t   ds;
    SYS_PORT_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n", __FUNCTION__);

    step = NetRxLenChkCtl_cfgMinLen1_f - NetRxLenChkCtl_cfgMinLen0_f;
    cmd = DRV_IOR(NetRxLenChkCtl_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &ds));
    field = GetNetRxLenChkCtl(V, cfgMinLen0_f + (step * index),&ds);
    *p_value = field;

    return CTC_E_NONE;
}

int32
sys_duet2_set_max_frame_size(uint8 lchip, ctc_frame_size_t index, uint16 value)
{
    int32  step = 0;
    uint32 cmd;
    uint32 field = 0;
    ds_t   ds;
    SYS_PORT_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n", __FUNCTION__);

    field = value;
    step = NetRxLenChkCtl_cfgMaxLen1_f - NetRxLenChkCtl_cfgMaxLen0_f;
    cmd = DRV_IOR(NetRxLenChkCtl_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &ds));
    SetNetRxLenChkCtl(V, cfgMaxLen0_f + (step * index),&ds, field);
    cmd = DRV_IOW(NetRxLenChkCtl_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &ds));

    return CTC_E_NONE;
}

int32
sys_duet2_get_max_frame_size(uint8 lchip, ctc_frame_size_t index, uint16* p_value)
{
    int32  step = 0;
    uint32 cmd;
    uint32 field = 0;
    ds_t   ds;

    SYS_PORT_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n", __FUNCTION__);

    step = NetRxLenChkCtl_cfgMaxLen1_f - NetRxLenChkCtl_cfgMaxLen0_f;
    cmd = DRV_IOR(NetRxLenChkCtl_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &ds));
    field = GetNetRxLenChkCtl(V, cfgMaxLen0_f + (step * index),&ds);
    *p_value = field;

    return CTC_E_NONE;
}

STATIC int32
_sys_duet2_get_min_framesize_index(uint8 lchip, uint16 size, uint8* p_index)
{
    uint8  idx = 0;
    uint16 value = 0;

    *p_index = CTC_FRAME_SIZE_MAX;

    for (idx = 0; idx < CTC_FRAME_SIZE_MAX; idx++)
    {
        CTC_ERROR_RETURN(sys_duet2_get_min_frame_size(lchip, idx, &value));

        if (size == value)
        {
            *p_index = idx;
            break;
        }
    }

    return CTC_E_NONE;
}

STATIC int32
_sys_duet2_get_max_framesize_index(uint8 lchip, uint16 size, uint8* p_index)
{
    uint8  idx = 0;
    uint16 value = 0;

    *p_index = CTC_FRAME_SIZE_MAX;

    for (idx = 0; idx < CTC_FRAME_SIZE_MAX; idx++)
    {
        CTC_ERROR_RETURN(sys_duet2_get_max_frame_size(lchip, idx, &value));

        if (size == value)
        {
            *p_index = idx;
            break;
        }
    }

    return CTC_E_NONE;
}

STATIC int32
_sys_duet2_port_get_chan_framesize_index(uint8 lchip, uint8 mac_id, uint8* p_index, sys_port_framesize_type_t type)
{
    uint32 index = 0;
    uint32 vlan_base = 0;
    uint32 cmd = 0;
    uint32 channelize_en = 0;
    DsChannelizeMode_m ds_channelize_mode;
    ds_t   ds;
    SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n", __FUNCTION__);

    sal_memset(&ds_channelize_mode, 0, sizeof(DsChannelizeMode_m));
    cmd = DRV_IOR(DsChannelizeMode_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, (mac_id & 0x3F), cmd, &ds_channelize_mode));
    GetDsChannelizeMode(A, channelizeEn_f, &ds_channelize_mode, &channelize_en);

    if (channelize_en)
    {
        if (SYS_PORT_FRAMESIZE_TYPE_MIN == type)
        {
            cmd = DRV_IOR(DsChannelizeIngFc_t, DRV_ENTRY_FLAG);
            CTC_ERROR_RETURN(DRV_IOCTL(lchip, (mac_id & 0x3F), cmd, &ds));
            index = GetDsChannelizeIngFc(V,chanMinLenSelId_f,&ds);
        }
        else
        {
            cmd = DRV_IOR(DsChannelizeIngFc_t, DRV_ENTRY_FLAG);
            CTC_ERROR_RETURN(DRV_IOCTL(lchip, (mac_id & 0x3F), cmd, &ds));
            index = GetDsChannelizeIngFc(V,chanMaxLenSelId_f,&ds);
        }
    }
    else
    {
        GetDsChannelizeMode(A, vlanBase_f, &ds_channelize_mode, &vlan_base);
        if (SYS_PORT_FRAMESIZE_TYPE_MIN == type)
        {
            index = (vlan_base >> 5) & 0x7;
        }
        else
        {
            index = (vlan_base >> 8) & 0x7;
        }
    }
    *p_index = index;

    return CTC_E_NONE;
}

STATIC int32
_sys_duet2_port_set_chan_framesize_index(uint8 lchip, uint8 mac_id, uint8 index, sys_port_framesize_type_t type)
{
    uint32 vlan_base = 0;
    uint32 cmd = 0;
    uint32 channelize_en = 0;
    uint32 drv_index = index;
    DsChannelizeMode_m ds_channelize_mode;
    ds_t   ds;
    SYS_PORT_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n", __FUNCTION__);
    SYS_PORT_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "Set index:%d, type:%d.\n", index, type);

    sal_memset(&ds_channelize_mode, 0, sizeof(DsChannelizeMode_m));
    cmd = DRV_IOR(DsChannelizeMode_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, (mac_id & 0x3F), cmd, &ds_channelize_mode));
    GetDsChannelizeMode(A, channelizeEn_f, &ds_channelize_mode, &channelize_en);

    if (channelize_en)
    {
        if (SYS_PORT_FRAMESIZE_TYPE_MIN == type)
        {
            cmd = DRV_IOR(DsChannelizeIngFc_t, DRV_ENTRY_FLAG);
            CTC_ERROR_RETURN(DRV_IOCTL(lchip, (mac_id & 0x3F), cmd, &ds));
            SetDsChannelizeIngFc(V,chanMinLenSelId_f,&ds,drv_index);
            cmd = DRV_IOW(DsChannelizeIngFc_t, DRV_ENTRY_FLAG);
            CTC_ERROR_RETURN(DRV_IOCTL(lchip, (mac_id & 0x3F), cmd, &ds));
        }
        else
        {
            cmd = DRV_IOR(DsChannelizeIngFc_t, DRV_ENTRY_FLAG);
            CTC_ERROR_RETURN(DRV_IOCTL(lchip, (mac_id & 0x3F), cmd, &ds));
            SetDsChannelizeIngFc(V,chanMaxLenSelId_f,&ds,drv_index);
            cmd = DRV_IOW(DsChannelizeIngFc_t, DRV_ENTRY_FLAG);
            CTC_ERROR_RETURN(DRV_IOCTL(lchip, (mac_id & 0x3F), cmd, &ds));

        }
    }
    else
    {
        GetDsChannelizeMode(A, vlanBase_f, &ds_channelize_mode, &vlan_base);
        if (SYS_PORT_FRAMESIZE_TYPE_MIN == type)
        {
            vlan_base &= ~(((uint16)0x7) << 5);
            vlan_base |= (index & 0x7) << 5;
        }
        else
        {
            vlan_base &= ~(((uint16)0x7) << 8);
            vlan_base |= (index & 0x7) << 8;
        }
        SetDsChannelizeMode(A, vlanBase_f, &ds_channelize_mode, &vlan_base);
        cmd = DRV_IOW(DsChannelizeMode_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, (mac_id & 0x3F), cmd, &ds_channelize_mode));
    }

    return CTC_E_NONE;
}

STATIC int32
_sys_duet2_select_chan_framesize_index(uint8 lchip, uint16 value, sys_port_framesize_type_t type, uint8* p_index)
{
    if (SYS_PORT_FRAMESIZE_TYPE_MIN == type)
    {
        CTC_ERROR_RETURN(_sys_duet2_get_min_framesize_index(lchip, value, p_index));
        if (CTC_FRAME_SIZE_MAX == *p_index)
        {
            return CTC_E_INVALID_PARAM;
        }
    }
    else
    {
        CTC_ERROR_RETURN(_sys_duet2_get_max_framesize_index(lchip, value, p_index));
        if (CTC_FRAME_SIZE_MAX == *p_index)
        {
            return CTC_E_INVALID_PARAM;
        }
    }

    return CTC_E_NONE;
}

STATIC int32
_sys_duet2_port_get_framesize(uint8 lchip, uint32 gport, uint16* p_value, sys_port_framesize_type_t type)
{

    uint8  index = 0;
    uint16 lport = 0;
    sys_datapath_lport_attr_t* p_port_cap = NULL;

    SYS_MAP_CTC_GPORT_TO_DRV_LPORT_WITH_CHECK(gport, lchip, lport);
    CTC_ERROR_RETURN(sys_usw_mac_get_port_capability(lchip, CTC_MAP_GPORT_TO_LPORT(gport), &p_port_cap));

    if (p_port_cap->port_type != SYS_DMPS_NETWORK_PORT)
    {
        SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " MAC %d is not used \n", p_port_cap->mac_id);
        return CTC_E_INVALID_CONFIG;

    }

    CTC_ERROR_RETURN(_sys_duet2_port_get_chan_framesize_index(lchip, p_port_cap->mac_id, &index, type));

    if (SYS_PORT_FRAMESIZE_TYPE_MIN == type)
    {
        CTC_ERROR_RETURN(sys_duet2_get_min_frame_size(lchip, index, p_value));
    }
    else
    {
        CTC_ERROR_RETURN(sys_duet2_get_max_frame_size(lchip, index, p_value));
    }

    return CTC_E_NONE;
}

STATIC int32
_sys_duet2_port_set_framesize(uint8 lchip, uint32 gport, uint16 value, sys_port_framesize_type_t type)
{
    uint8  index = 0;
    uint16 lport = 0;
    uint32 enable = 0;

    sys_datapath_lport_attr_t* p_port_cap = NULL;

    SYS_MAP_CTC_GPORT_TO_DRV_LPORT_WITH_CHECK(gport, lchip, lport);
    CTC_ERROR_RETURN(sys_usw_mac_get_port_capability(lchip, CTC_MAP_GPORT_TO_LPORT(gport), &p_port_cap));

    if (p_port_cap->port_type != SYS_DMPS_NETWORK_PORT)
    {
        SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " MAC %d is not used \n", p_port_cap->mac_id);
        return CTC_E_INVALID_CONFIG;

    }

    CTC_ERROR_RETURN(sys_usw_mac_get_mac_en(lchip, lport, &enable));
    if (TRUE == enable)
    {
        SYS_MAC_DBG_OUT(CTC_DEBUG_LEVEL_ERROR, " To change max/min framesize, the mac must be disable\n");
        return CTC_E_NOT_READY;
    }

    CTC_ERROR_RETURN(_sys_duet2_select_chan_framesize_index(lchip, value, type, &index));
    CTC_ERROR_RETURN(_sys_duet2_port_set_chan_framesize_index(lchip, p_port_cap->mac_id, index, type));

    return CTC_E_NONE;
}

STATIC int32
_sys_duet2_port_set_max_frame(uint8 lchip, uint32 gport, uint32 value)
{
    CTC_ERROR_RETURN(_sys_duet2_port_set_framesize(lchip, gport, value, SYS_PORT_FRAMESIZE_TYPE_MAX));

    return CTC_E_NONE;
}

STATIC int32
_sys_duet2_port_get_max_frame(uint8 lchip, uint32 gport, uint32* p_value)
{
    uint16 value = 0;

    CTC_ERROR_RETURN(_sys_duet2_port_get_framesize(lchip, gport, &value, SYS_PORT_FRAMESIZE_TYPE_MAX));
    *p_value = value;

    return CTC_E_NONE;
}

STATIC int32
_sys_duet2_port_set_min_frame_size(uint8 lchip, uint32 gport, uint8 size)
{
    CTC_ERROR_RETURN(_sys_duet2_port_set_framesize(lchip, gport, size, SYS_PORT_FRAMESIZE_TYPE_MIN));

    return CTC_E_NONE;
}

STATIC int32
_sys_duet2_port_get_min_frame_size(uint8 lchip, uint32 gport, uint8* p_size)
{
    uint16 size = 0;

    CTC_ERROR_RETURN(_sys_duet2_port_get_framesize(lchip, gport, &size, SYS_PORT_FRAMESIZE_TYPE_MIN));
    *p_size = size;

    return CTC_E_NONE;
}
int32
sys_duet2_port_set_property(uint8 lchip, uint32 gport, ctc_port_property_t port_prop, uint32 value)
{
    int32   ret = CTC_E_NONE;
    uint8   value8 = 0;
    SYS_PORT_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n", __FUNCTION__);
    SYS_PORT_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "Set port property, gport:0x%04X, property:%d, value:%d\n", \
                     gport, port_prop, value);

    switch (port_prop)
    {
    case CTC_PORT_PROP_MAX_FRAME_SIZE:
        ret = _sys_duet2_port_set_max_frame(lchip, gport, value);
        break;

    case CTC_PORT_PROP_MIN_FRAME_SIZE:
        value8 = value;
        ret = _sys_duet2_port_set_min_frame_size(lchip, gport, value8);
        break;
    default:
        return CTC_E_NOT_SUPPORT;
    }

    if (ret != CTC_E_NONE)
    {
        return ret;
    }

    return CTC_E_NONE;
}
/**
@brief    Get port's properties according to gport id
*/
int32
sys_duet2_port_get_property(uint8 lchip, uint32 gport, ctc_port_property_t port_prop, uint32* p_value)
{
    uint8  value8 = 0;
    int32  ret = CTC_E_NONE;
    uint16 lport = 0;
    SYS_PORT_DBG_OUT(CTC_DEBUG_LEVEL_FUNC, "%s()\n", __FUNCTION__);
    SYS_PORT_DBG_OUT(CTC_DEBUG_LEVEL_INFO, "Get port property, gport:0x%04X, property:%d!\n", gport, port_prop);

    /*Sanity check*/
    SYS_MAP_CTC_GPORT_TO_DRV_LPORT_WITH_CHECK(gport, lchip, lport);
    /* zero value */
    *p_value = 0;

    switch (port_prop)
    {
    case CTC_PORT_PROP_MAX_FRAME_SIZE:
        ret = _sys_duet2_port_get_max_frame(lchip, gport, (ctc_frame_size_t*)p_value);
        break;
    case CTC_PORT_PROP_MIN_FRAME_SIZE:
        ret = _sys_duet2_port_get_min_frame_size(lchip, gport, &value8);
        *p_value = value8;
        break;
    default:
        return CTC_E_NOT_SUPPORT;
    }

    if (ret != CTC_E_NONE)
    {
        return ret;
    }

    return CTC_E_NONE;
}

STATIC int32
_sys_duet2_port_framesize_init(uint8 lchip)
{
    uint8  idx = 0;
    uint32 valueMin = SYS_USW_MIN_FRAMESIZE_DEFAULT_VALUE;

    for (idx = 0; idx < CTC_FRAME_SIZE_MAX; idx++)
    {
        if(CTC_FRAME_SIZE_7 == idx )
        {
            valueMin = SYS_USW_ILOOP_MIN_FRAMESIZE_DEFAULT_VALUE;
        }
        else
        {
            valueMin = SYS_USW_MIN_FRAMESIZE_DEFAULT_VALUE;
        }
        CTC_ERROR_RETURN(sys_duet2_set_min_frame_size(lchip, idx, valueMin));
        CTC_ERROR_RETURN(sys_duet2_set_max_frame_size(lchip, idx, SYS_USW_MAX_FRAMESIZE_DEFAULT_VALUE));
    }

    return CTC_E_NONE;
}
STATIC int32
_sys_duet2_port_chan_init(uint8 lchip)
{
    uint8  index = 0;
    uint16 lport = 0;
    uint32 field = 0;
    uint32 cmd = 0;
    uint32 cmd1 = 0;
    sys_datapath_lport_attr_t* p_port_cap = NULL;
    NetRxCtl_m net_rx_ctl;
    NetTxMiscCtl_m net_tx_misc_ctl;
    EpeHeaderEditPhyPortMap_m  epe_map;
    int32 ret = 0;
    ds_t  ds;
    CTC_ERROR_RETURN(_sys_duet2_port_framesize_init(lchip));

    sal_memset(&epe_map, 0, sizeof(epe_map));
    SetEpeHeaderEditPhyPortMap(V, portVlanBase_f, &epe_map, SYS_USW_MAX_VLAN_BASE);
    cmd1 = DRV_IOW(EpeHeaderEditPhyPortMap_t, DRV_ENTRY_FLAG);
    
    for (lport = 0; lport < MCHIP_CAP(SYS_CAP_PER_SLICE_PORT_NUM); lport++)
    {
        ret = (sys_usw_mac_get_port_capability(lchip, lport,&p_port_cap));
        if (ret == CTC_E_INVALID_CONFIG)
        {
            continue;
        }

        if (p_port_cap->port_type != SYS_DMPS_NETWORK_PORT)
        {
            continue;
        }

        CTC_ERROR_RETURN(_sys_duet2_port_set_chan_framesize_index(lchip, p_port_cap->chan_id, index, SYS_PORT_FRAMESIZE_TYPE_MAX));
        CTC_ERROR_RETURN(_sys_duet2_port_set_chan_framesize_index(lchip, p_port_cap->chan_id, index, SYS_PORT_FRAMESIZE_TYPE_MIN));

        field = lport & 0x3F;
        cmd = DRV_IOR(EpeHeaderAdjustPhyPortMap_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, (p_port_cap->chan_id), cmd, &ds));
        SetEpeHeaderAdjustPhyPortMap(V,localPhyPort_f,&ds,field);
        cmd = DRV_IOW(EpeHeaderAdjustPhyPortMap_t, DRV_ENTRY_FLAG);
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, (p_port_cap->chan_id), cmd, &ds));
        CTC_ERROR_RETURN(DRV_IOCTL(lchip, (p_port_cap->chan_id), cmd1, &epe_map));
    }

    sal_memset(&net_rx_ctl, 0, sizeof(NetRxCtl_m));
    cmd = DRV_IOR(NetRxCtl_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &net_rx_ctl));

    SetNetRxCtl(V, underLenChkEn_f, &net_rx_ctl, 1);
    SetNetRxCtl(V, cfgOverLenWithErr_f, &net_rx_ctl, 1);
    SetNetRxCtl(V, bpduChkEn_f, &net_rx_ctl, 0);

    cmd = DRV_IOW(NetRxCtl_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &net_rx_ctl));

    sal_memset(&net_tx_misc_ctl, 0, sizeof(NetTxMiscCtl_m));
    cmd = DRV_IOR(NetTxMiscCtl_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &net_tx_misc_ctl));

    SetNetTxMiscCtl(V, runtDropEn_f, &net_tx_misc_ctl, 1);
    /* RTL logic is discard packet, when packet length <= cfgMinPktLen */
    SetNetTxMiscCtl(V, cfgMinPktLen0_f, &net_tx_misc_ctl, (MCHIP_CAP(SYS_CAP_PORT_MIN_FRAMESIZE_MIN_VALUE) - 1));

    cmd = DRV_IOW(NetTxMiscCtl_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &net_tx_misc_ctl));

    /* RTL logic is discard packet, when egressHeaderAdjust output packet's length <= minPktLen */
    field = 5;
    cmd = DRV_IOR(EpeHdrAdjustMiscCtl_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &ds));
    SetEpeHdrAdjustMiscCtl(V,minPktLen_f,&ds,field);
    cmd = DRV_IOW(EpeHdrAdjustMiscCtl_t, DRV_ENTRY_FLAG);
    CTC_ERROR_RETURN(DRV_IOCTL(lchip, 0, cmd, &ds));

    return CTC_E_NONE;
}
int32
sys_duet2_port_init(uint8 lchip)
{
    CTC_ERROR_RETURN(_sys_duet2_port_chan_init(lchip));
    return CTC_E_NONE;
}


